#include "MyMap.h"
#include <iostream>
#include <assert.h>
#include <string>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <crypt.h>
#include <stdlib.h>
#include <string.h>
#include <curses.h>
#include "SnakePlayer.h"

using namespace std;

const char * MyMap::mapBeginnerString = "#MAP#";

const char MyMap::apple = 'a';
const char MyMap::level_entrance = 'l';
const char MyMap::mine = 'm';
const char MyMap::bonus_pill = 'b';
const char MyMap::star = 's';
const char MyMap::empty_field = '.';
const char MyMap::declared_for_snake = 'd';
const char MyMap::changed_state = 'S';
const char MyMap::not_changed_state = ' ';
const char MyMap::map_error_char='E';
const int MyMap::snake_winner_none = -1;
const int MyMap::snake_winner_all_lost = -2;

MyMap * MyMap::instance= NULL;

void MyMap::generateMap()
{
	int x, y;

	if (true == load_static_map)
	{
		setMapCharAt(mine, 10, 10);
		setMapCharAt(mine, 10, 11);
		setMapCharAt(mine, 10, 12);
		setMapCharAt(mine, 10, 13);
		setMapCharAt(mine, 10, 14);
		setMapCharAt(mine, 10, 16);
		setMapCharAt(mine, 10, 17);
		setMapCharAt(mine, 10, 18);
		setMapCharAt(mine, 10, 19);
		setMapCharAt(mine, 11, 19);
		setMapCharAt(mine, 11, 20);
		setMapCharAt(mine, 11, 21);

		setMapCharAt(mine, 50, 7);
		setMapCharAt(mine, 50, 8);
		setMapCharAt(mine, 50, 9);
		setMapCharAt(mine, 50, 10);
		setMapCharAt(mine, 51, 11);
		setMapCharAt(mine, 52, 12);
		setMapCharAt(mine, 53, 13);
		setMapCharAt(mine, 54, 14);
		setMapCharAt(mine, 55, 16);
		setMapCharAt(mine, 56, 17);
		setMapCharAt(mine, 57, 18);
		setMapCharAt(mine, 58, 19);
		setMapCharAt(mine, 61, 19);
		
		setMapCharAt(mine, 62, 29);
		setMapCharAt(mine, 63, 29);
	}
	else
	{
		//dynamic generation
		for (int i = 0; i < this->applesAmount; ++i)
		{
			do
			{
				x = rand() % MAP_X + 1; //generate the first apple
				y = rand() % MAP_Y + 1;
			} while (!isEmptyAt(x, y));
			setMapCharAt(apple, x, y);
		}
		for (int loop1 = 0; loop1 < mines_amount; loop1++) //create as many mines as needed
		{
			do
			{
				x = rand() % MAP_X + 1;
				y = rand() % MAP_Y + 1;
			} while (!isEmptyAt(x, y));
			setMapCharAt(MyMap::mine, x, y);

		}
	}
}
MyMap * MyMap::instanceOf()
{

	if (instance == NULL)
	{
		instance = new MyMap();
	}
	return instance;
}

MyMap * MyMap::getInstance()
{
	return MyMap::instanceOf();
}

MyMap::MyMap(void)
{
	reset();
	cleanupLastChanges();

	mines_amount = 10;
	applesAmount = 0;
	this->serverMap = true;
	load_static_map = true;
	generateMap();
}

void MyMap::reset()
{
	for (int i = 0; i < MAP_Y; ++i)
	{
		for (int j = 0; j < MAP_X; ++j)
		{
			map[j][i] = MyMap::empty_field;
		}//koniec fora po xach

	}
}

#if 0
void MyMap::fillRandomNumbers()
{
	for(int i=0;i < MAP_Y;++i)
	{
		for(int j=0;j<MAP_X;++j)
		{
			map[j][i]='x';

		}//koniec fora po xach

	}
	map[0][0]='o';
}
#endif

MyMap::~MyMap(void)
{
}

std::string MyMap::serialize()
{
	std::string toRet = "";

	for (int i = 0; i < MAP_Y; ++i)
	{
		for (int j = 0; j < MAP_X; ++j)
		{
			toRet += map[j][i];
			toRet += ",";
		}//koniec fora po xach
		toRet+="\n";
	}//koniec fora po ykach

	toRet += "@";
	////cout <<toRet;
	return toRet;
}

void MyMap::deserializeFrom(std::string &param)
{
	/* note - zmienione z int na uint*/
	uint indexOfMap = param.find(mapBeginnerString);

	if (indexOfMap == string::npos)
	{
		cerr
				<< "Blad przy deserializowaniu mapy - nie moge znalezc poczatku listy mapy....\n";
		return;
	}
	indexOfMap = indexOfMap + strlen(mapBeginnerString);//zeby znalezc sie na pierwszym zczytywanym polu po stringu
	////cout<< param.at(indexOfMap)<<endl;

	//teraz dziele string ',' (przecinkami) i ustawiam kolejne wartosci

	/* note - zmienione z int na uint*/
	uint indexOfMapFieldEnds = 0;
	string currentField = "";

	for (int i = 0; i < MAP_Y; ++i)
	{
		for (int j = 0; j < MAP_X; ++j)
		{

			indexOfMapFieldEnds = param.find(',', indexOfMap + 1);
			////cout<< "ie|"<<param.at(indexOfMapFieldEnds)<<"|"<<endl;

			if (indexOfMapFieldEnds == string::npos)
			{
				////cout <<"Koniec deserializacji..."<<endl;
				break;
			}

			if (param.at(indexOfMap) == ',')
			{
				indexOfMap++;//jesli tak to znaczy ze kursor zatrzymal sie za wczesnie
			}
			//substr za drugi param bierze ilosc znakow wiec trzeba odjac pierwszy param
			////cout<<":"<<param.substr(indexOfMap,indexOfMapFieldEnds-indexOfMap);
			currentField = param.substr(indexOfMap,
					indexOfMapFieldEnds - indexOfMap);

			map[j][i] = atoi(currentField.c_str());

			indexOfMap = indexOfMapFieldEnds;
		}//koniec fora po xach

	}//koniec fora po ykach

}

char MyMap::getMapCharAt(int x, int y)
{
	if (validateMapCoords(x, y) )
	{
		return map[x][y];
	}
	else
	{
		return MyMap::map_error_char;
	}
}

bool MyMap::isAppleAt(int x, int y)
{
	if (getMapCharAt(x, y) == MyMap::apple)
	{
		return true;
	}
	return false;
}

bool MyMap::isMineAt(int x, int y)
{

	if (getMapCharAt(x, y) == MyMap::mine)
	{
		////printf("Checking..Mine is at: (%d %d)\n",x,y);
		return true;
	}

	return false;
}

bool MyMap::isBonusAt(int x, int y)
{
	if (getMapCharAt(x, y) == MyMap::bonus_pill)
	{
		return true;
	}
	return false;
}
bool MyMap::isStarAt(int x, int y)
{
	if (getMapCharAt(x, y) == MyMap::star)
	{
		return true;
	}
	return false;
}

bool MyMap::isEmptyAt(int x, int y)
{
	if (getMapCharAt(x, y) == MyMap::empty_field)
	{
		return true;
	}
	return false;
}

void MyMap::setMapCharAt(char c, int x, int y)
{
	assert (1 == validateMapCoords(x, y) );
	if ( 1 == validateMapCoords(x, y) )
	{
		/* sprawdzam stan - zaznaczam jako changed jesli sie zmienil*/
		if (getMapCharAt(x, y) != c)
		{
			markAsChanged(x, y);
			map[x][y] = c;
		}
		else
		{
			/*stan sie nie zmienil - nic nie robie */
		}

	}
}
void MyMap::debugMap()
{
	//cout <<"Debug map\n"<<endl;

	for (int yi = 0; yi < MAP_Y; ++yi)
	{
		for (int xi = 0; xi < MAP_X; ++xi)
		{
			//cout << map[xi][yi];

		}//koniex for x
		//cout<<endl;
	}//koniec for y


	//cleanSnakeData();
	vector<SnakePlayer *>::iterator it;
	it = snakesPlayers.begin();

	while (it != snakesPlayers.end())
	{
		(*it)->drawMeOnMap();
		it++;

	}

}

SnakeSegment * MyMap::generateFieldForSnake()
{
	SnakeSegment *s;
	int x, y;

	do
	{
		x = rand() % MAP_X + 1; //generate the first apple
		y = rand() % MAP_Y + 1;
	} while (!isEmptyAt(x, y));

	s = new SnakeSegment(x, y);
	setMapCharAt(MyMap::declared_for_snake, x, y);
	return s;
}

SnakeSegment * MyMap::generateNextAppleOnMap()
{
	SnakeSegment * s;
	int x, y;

	do
	{
		x = rand() % MAP_X + 1; //generate the first apple
		y = rand() % MAP_Y + 1;
	} while (!isEmptyAt(x, y));

	s = new SnakeSegment(x, y);
	//s.x=x;
	//s.y=y;
	setMapCharAt(MyMap::apple, x, y);
	return s;
}

void MyMap::cleanSnakeData()
{
	for (int yi = 0; yi < MAP_Y; ++yi)
	{
		for (int xi = 0; xi < MAP_X; ++xi)
		{
			if (map[xi][yi] != MyMap::apple && map[xi][yi]
					!= MyMap::empty_field && map[xi][yi]
					!= MyMap::bonus_pill && map[xi][yi]
					!= MyMap::mine && map[xi][yi]
					!= MyMap::level_entrance)
			{

				map[xi][yi] = MyMap::empty_field;
			}//end if

		}//koniex for x
	}//koniec for y

}

Direction MyMap::getOppositeDirectionFor(Direction d)
{

	if (SNAKE_DOWN == d)
		return SNAKE_UP;

	if (SNAKE_UP == d)
		return SNAKE_DOWN;

	if (SNAKE_LEFT == d)
		return SNAKE_RIGHT;

	if (SNAKE_RIGHT == d)
		return SNAKE_LEFT;

	/* bu defauld return NULL*/
	/* this should NOT HAPPEN*/
	assert(0);
	return SNAKE_UP;
}

SnakeSegment MyMap::getSegmentAtDirection(Direction d, int x, int y)
{

	SnakeSegment s;
	s.x = x;
	s.y = y;
	assert(x>=0);
	assert(y>=0);

	if (d == SNAKE_DOWN)
	{
		//operating on y

		if (y < MAP_Y-1 && y >= 0)
		{

			s.y = y + 1;
		}
		else
		{
			s.y = 0;
		}

	}//end SNAKE_UP

	if (d == SNAKE_UP)
	{
		if (y <= MAP_Y -1 && y > 0)
		{

			s.y = y - 1;
		}
		else
		{
			s.y = MAP_Y-1;
		}

	}//end SNAKE_DOWN


	if (d == SNAKE_LEFT)
	{
		if (x <= MAP_X -1 && x > 0)
		{
			s.x = x - 1;
		}
		else
		{
			s.x = MAP_X-1;
		}
	}//end SNAKE LEFT

	if (d == SNAKE_RIGHT)
	{
		if (x < MAP_X -1 && x >= 0)
		{
			s.x = x + 1;
		}
		else
		{
			s.x = 0;
		}
	}//end SNAKE_RIGHT
	return s;
}

bool MyMap::checkBelongsToSnakes(int x, int y)
{

	vector<SnakePlayer *>::iterator it;

	it = snakesPlayers.begin();

	while (it != snakesPlayers.end())
	{

		if ((*it)->isThatMySegment(x, y))
		{
			return true;
		}

		it++;

	}
	return false;
}
void MyMap::registerSnakePlayer(SnakePlayer *sp)
{
	////printf("Register player in map..OK");
	snakesPlayers.push_back(sp);
}

void MyMap::unregisterSnakePlayer(SnakePlayer *sp)
{

	vector<SnakePlayer *>::iterator it;

	it = snakesPlayers.begin();

	while (it != snakesPlayers.end())
	{
		if (sp == (*it))
		{
			////printf("Unregistered player #%d succesfully",sp->getNumber());
			snakesPlayers.erase(it);
		}
		it++;

	}

}

void MyMap::debugRegisteredPlayers()
{

	vector<SnakePlayer *>::iterator it;

	it = snakesPlayers.begin();

	////printf("Players registered in game\n");
	while (it != snakesPlayers.end())
	{

		////printf("Player #%d,gameOver: %d\n",(*it)->getNumber(),(*it)->getGameOver());


		it++;

	}
}

void MyMap::printSnakesOnMap()
{

	vector<SnakePlayer *>::iterator it;

	it = snakesPlayers.begin();
	//this->cleanSnakeData();
	////printf("Players registered in game\n");
	while (it != snakesPlayers.end())
	{
		(*it)->drawMeOnMap();
		it++;

	}
}

void MyMap::cleanupLastChanges()
{

	for (int i=0; i < MAP_X; ++i)
	{
		for (int j=0; j < MAP_Y; ++j)
		{
			last_state_map[i][j] = MyMap::not_changed_state;
		}
	}
}

void MyMap::markAllChanged()
{

	for (int i=0; i < MAP_X; ++i)
	{
		for (int j=0; j < MAP_Y; ++j)
		{
			last_state_map[i][j] = MyMap::changed_state;
		}
	}
}

void MyMap::markAsChanged(int x, int y)
{
	if (x < MAP_X && y < MAP_Y)
	{
		last_state_map[x][y] = MyMap::changed_state;
	}
}
/* resetuje wszystkie dane wezy i rezczy ktore sa na mapie*/
void MyMap::resetMapAndSnakes()
{

	setRoundNumber(0);
	cleanSnakeData();
	cleanupLastChanges();
	vector<SnakePlayer *>::iterator it;

	it = snakesPlayers.begin();

	////printf("Players registered in game\n");
	while (it != snakesPlayers.end())
	{
		(*it)->reset();
		it++;

	}
}

char MyMap::getMapCharIfChanged(int x, int y)
{
	if ( 1 == validateMapCoords(x, y) )
	{
		if (MyMap::changed_state == last_state_map[x][y])
		{
			return getMapCharAt(x, y);
		}
		else
		{
			/* char is valid but nothing changed since last iter*/
			return MyMap::not_changed_state;
		}
	}
	else
	{
		/* map coords not valid*/
		return MyMap::map_error_char;
	}
}

int MyMap::validateMapCoords(int x, int y)
{
	if (x < MAP_X && y < MAP_Y && x >= 0 && y >= 0)
	{

		return 1;
	}
	else
	{
		return 0;
	}
}

SnakePlayer * MyMap::getSnakePlayerByNumber(int number)
{
	vector<SnakePlayer *>::iterator it;
	SnakePlayer * to_ret= NULL;

	it = snakesPlayers.begin();

	while (it != snakesPlayers.end())
	{
		/* get snake by its internal number*/
		if ((*it)->getNumber() == number)
		{
			to_ret = (*it);
			break;
		}

		it++;

	}
	/* return found snake or NULL if not able */
	return to_ret;
}

void MyMap::setRoundNumber(int number)
{
	round_number = number;
}

int MyMap::getRoundNumber()
{
	return round_number;
}

int MyMap::incrementRoundNumber()
{
	round_number++;

	return round_number;
}

int MyMap::getWinPlayerNumber()
{
	vector<SnakePlayer *>::iterator it;

	it = snakesPlayers.begin();
	int winner_amount = 0;
	int snake_winner_id = -1;
	bool all_lost = true;

	while (it != snakesPlayers.end())
	{
		/* get snake by its internal number*/
		if ( false == (*it)->getGameOver())
		{
			winner_amount++;
			snake_winner_id = (*it)->getNumber();
			all_lost = false;
		}

		it++;

	}

	if ( true == all_lost)
	{
		return MyMap::snake_winner_all_lost;
	}
	/* exactly ONE winner*/
	if (1 == winner_amount)
	{
		return snake_winner_id;
	}
	else
	{
		return MyMap::snake_winner_none;
	}
}
